package space

import (
	"errors"
	"src/zeus/common"
	"src/zeus/entity"
	"src/zeus/iserver"
	"src/zeus/linmath"
	"src/zeus/logger"
	"src/zeus/pathfinder"
	"src/zeus/timer"
	. "src/zeus/timer"
	"src/zeus/timerwheel"
	"time"
)

type IRealSpace interface {
	//地图加载成功的回调
	OnMapLoadSucceed()
	//加载失败的回调
	OnMapLoadFailed()
	//获取最小坐标
	GetMinPos() (x, y int)
	//获取最大坐标
	GetMaxPos() (x, y int)
	//地图Coord坐标系格子的大小
	GetSize() (size int)

	// 准备场景，回到初始化的状态
	Prepare()
	//回收场景，需要把场景中的定时器给关了
	Recycle()
	//销毁场景
	OnDestroy()
	// 人数变化时
	OnPlayerChanges()
	// 设置在Loop结束的时候，需要广播发消息的方法（主要用于技能有关的RPC消息）
	SetLoopedMsg(f func())
}

// Space 空间实体
type Space struct {
	entity.Entity    //自己也是一个ntt对象
	*entity.Entities //自己也一个nnt管理器
	iserver.ICoord   //9宫格系统
	*TimeTicker      //当前时间
	//时间轮
	Timer *timerwheel.Timer

	//寻路算法有关
	PathFindermgr pathfinder.IPathFinder
	//地图数据，包括寻路等系统
	MapInfo      *Map
	MapName      string
	MapKey       string
	loadObstacle int32 // 是否需要加载动态阻挡
	//非玩家对象集合
	tinyEntities map[uint64]iTinyEntity
	//开始时间
	StartTime time.Time

	isMapLoaded bool
	// 场景广播消息队列
	delayedCastMsgs []*DelayedCastNotifyMsg
	//有脏数据对象
	DirtyTinyList []iserver.IDirty
	// //redis操作
	// Util *sceneDis.SceneUtil
	// Loop结束的时候，用于生成广播消息的方法（主要用于技能有关的RPC消息）
	LoopSendMsgList []func()
}

// ----------------------------------------------------------------
//设置场景人数

// 人数变化时
func (s *Space) OnPlayerChanges() {
	s.regSpaceSrvID()
}

// 设置场景人数
func (s *Space) regSpaceSrvID() {
	// if s.Util != nil {
	// 	s.Util.SetNum(s.EntityCount())
	// }
}

// 清空地图信息
func (s *Space) unRegSpaceSrvID() {
	// if s.Util != nil {
	// 	s.Util.ClearMap(s.GetID())
	// }
}

//设置场景人数
// ----------------------------------------------------------------
// 加载、卸载、Panic等逻辑
/*
	Space 本身是一个实体，但是消息相关的几个函数都由Entities来执行
	主要原因是为了方便的使用几个Entities的默认处理函数
	如Entity的消息传送等等
*/
// MainLoop 主循环
func (s *Space) MainLoop() {
	s.Entity.MainLoop()
}

// OnLoop 完全覆盖Entity的Loop方法
func (s *Space) OnLoop() {
	curTime := s.GetNowTime()
	// s.TimeTicker.TimeTick(curTime)
	s.Timer.OnTick(curTime)
	s.Entity.OnLoop()
	s.Entities.MainLoop()

	s.LoopSend()

	s.Entities.Range(func(k, v interface{}) bool {
		if iA, ok := v.(iLooper); ok {
			iA.updateAOI()
			//因为只对自己发消息，所以可以把这二个遍历都合并了
			iA.onLateLoop()
		}
		return true
	})

	// s.Entities.Range(func(k, v interface{}) bool {
	// 	if iL, ok := v.(iLateLooper); ok {
	// 		iL.onLateLoop()
	// 	}
	// 	return true
	// })
	s.DoBroadCastMsg()

	// //更新人数信息
	// if s.Util != nil {
	// 	s.Util.Loop()
	// }
	s.TimeTicker.EndTick()
}

func (this *Space) LoopSend() {
	defer func() {
		// clear(this.LoopSendMsgList)
		this.LoopSendMsgList = this.LoopSendMsgList[0:0]
	}()
	// 设置需要广播的消息
	li := this.LoopSendMsgList
	for _, f := range li {
		f()
	}
}

// 设置在Loop结束的时候，需要广播发消息的方法（主要用于技能有关的RPC消息）
func (this *Space) SetLoopedMsg(f func()) {
	this.LoopSendMsgList = append(this.LoopSendMsgList, f)
}

func (s *Space) OnPostEntityCreated() {
	/*
		场景的每帧时间希望固定下来，不要因为系统的原因变化
	*/
	defer func() {
		if err := recover(); err != nil {
			logger.Fatal("加载场景报错：", s.MapName, err)
		}
	}()
	s.Entity.OnPostEntityCreated()
	s.StartTime = iserver.GetSrvInst().GetNowTime()
	v := int64(iserver.GetSrvInst().GetFrameDeltaTime() / time.Millisecond)
	s.TimeTicker = timer.NewTimeTicker(s.StartTime, v)
	s.Timer = timerwheel.NewTimer(timerwheel.TimerSetAccuracy(v),
		timerwheel.TimerSetNow(s.StartTime))

	s.ICoord = NewTileCoord(s.GetTileSize())
	s.ICoord.SetAnchor(*s.GetLevelMiniLocation())
	s.Entities = entity.NewEntities(false, s.Entity.MsgHandler, s, 10)
	s.Entities.Init()
	// s.IEntities = s.IEntities
}

// OnInit 初始化
func (s *Space) OnInit() {
	// 暂时先写一个最大的尺寸，后期应该从maploaded结束后再初始化
	// s.tinyEntities = &sync.Map{}
	s.tinyEntities = make(map[uint64]iTinyEntity)
	s.isMapLoaded = false

	s.RegMsgProc(&SpaceMsgProc{space: s})

	s.Entity.OnInit()
	// s.Util = dbservice.NewSceneUtil(iserver.GetSrvInst().GetSrvID())
	// s.regSpaceSrvID()

	// s.Timer.Start()
}

// OnAfterInit 逻辑层初始化完成之后, 再启动逻辑协程
func (s *Space) OnAfterInit() {

	s.Entity.OnAfterInit()
	s.loadFinder()
	// s.loadMap()
}

// OnMapLoadSucceed 地图加载成功, 框架层回调
func (s *Space) OnMapLoadSucceed() {
	// s.PathFindermgr = pathfinder.NewFindNavmeshCpp(s.MapInfo.Cgo_navmesh, s.MapInfo.Cgo_TONavmesh)
	// s.PathFindermgr = pathfinder.NewFindNavmeshCpp(nil, nil)

}

// 加载 失败
func (s *Space) OnMapLoadFailed() {
	logger.Error("地图加载失败 ", s.GetRealPtr())

}

// OnDestroy 析构函数
func (s *Space) OnDestroy() {
	s.Entities.Destroy()
	s.Entity.OnDestroy()
	for _, v := range s.tinyEntities {
		v.onDestroy()
	}
	s.delayedCastMsgs = nil
	s.MapInfo = nil
	s.PathFindermgr.Destroy()
	s.Timer.DeleteAllTimerTask()
	// if s.Util != nil {
	// 	s.Util.Destroy()
	// }
	// 调用顺序的关系，后面tinyentity执行destroy操作的时候，还是会调用到tinyEntities，所以这里不置为空
	//s.tinyEntities = nil
}

// OnPanic 宕机时调用
func (s *Space) OnPanic(err error) {
	// s.Entity.OnPanic()
	// iserver.GetSrvInst().DestroyEntity(s.GetID())
	//TODO 这里删除掉挂掉的space,但是需要考虑是否要新建场景加到场景管理器里去 (这里后面要改成主城不销毁，level销毁后，新建场景加入)
}

// 准备场景，回到初始化的状态
func (this *Space) Prepare() {
	// this.TimeTicker.TimeTick(this.GetNowTime())
	this.TimeTicker.EndTick()
	this.GetTimer().ReStart(this.GetCurTime())
	this.regSpaceSrvID()
	this.SetEntityState(iserver.Entity_State_Loop)
}

// 回收场景，需要把场景中的定时器给关了
func (tb *Space) Recycle() {
	tb.SetEntityState(iserver.Entity_State_Suspend)
	tb.GetTimer().Stop()
	tb.PathFindermgr.RemoveAllObstacle()
	tb.PathFindermgr.UpdateObstacle()
	/*
		这里还需要关掉其他东西
	*/
	tb.DestroyAllTinyEntity()
	tb.DestroyAllEntity()
	tb.unRegSpaceSrvID()
	for i := 0; i <= 10 && tb.DelayCallHost.GetDelayCallHandle().Len() > 0; i++ {
		tb.DelayCallHost.GetDelayCallHandle().CallDelayExec2()
		if i > 0 {
			logger.Warn("DelayCallHost")
		}
	}
	// 由于DestroyEntity是CallDalay性质的,这里也应该CallDelay
	if l := tb.GetTimer().Len(); l > 0 {
		logger.Warn("Timer len >0;", tb.GetTimer())
	}

	tb.GetTimer().DeleteAllTimerTask()
	// clear(tb.LoopSendMsgList)
	tb.LoopSendMsgList = tb.LoopSendMsgList[0:0]
}

// // 设置redis操作对象
// func (this *Space) SetUtil(u *sceneDis.SceneUtil) {
// 	// this.Util = u
// }

// 把entity的这个函数的逻辑去掉
func (this *Space) ReflushSync() {

}

//加载、卸载、Panic等逻辑
//----------------------------------------------------------------
// 成员管理

// AddEntity 在空间中添加entity
func (s *Space) AddEntity(entityType string, entityID uint64, dbid uint64, initParam interface{}, syncInit bool, fly bool) error {
	e, err := s.CreateEntity(entityType, entityID, dbid, s.GetID(), initParam, syncInit, fly)
	if err != nil {
		return err
	}

	_, ok := e.(iserver.ISpaceEntity)
	if !ok {
		s.DestroyEntity(e.GetID())
		return errors.New("the entity which add to space must be ISpaceEntity ")
	}

	return nil
}

// CreateEntity 创建实体
func (s *Space) CreateEntity(entityType string, entityID uint64, dbid uint64, spaceID uint64, initParam interface{}, syncInit bool, fly bool) (result iserver.IEntity, err error) {
	result, err = s.Entities.CreateEntity(entityType, entityID, dbid, spaceID, initParam, syncInit, fly)
	if err == nil {
		s.GetRealPtr().(IRealSpace).OnPlayerChanges()
	}
	return
}

func (s *Space) DestroyEntity(entityID uint64) (err error) {
	err = s.Entities.DestroyEntity(entityID)
	if err == nil {
		s.GetRealPtr().(IRealSpace).OnPlayerChanges()
	}
	return
}

func (s *Space) DestroyAllEntity() {
	s.Entities.Range(
		func(k, v interface{}) bool {
			if err := s.Entities.DestroyEntity(k.(uint64)); err != nil {
				logger.Error(err)
			}
			return true
		})
}
func (s *Space) DestroyAllTinyEntity() {
	for _, v := range s.tinyEntities {
		v.onDestroy()
	}
	s.tinyEntities = make(map[uint64]iTinyEntity)
}

// RemoveEntity 在空间中删除entity
func (s *Space) RemoveEntity(entityID uint64) error {

	e := s.GetEntity(entityID)
	if e == nil {
		return errors.New("no entity exist")
	}

	return s.DestroyEntity(entityID)
}

// RemoveEntityByDBID 在空间中删除entity
func (s *Space) RemovePlayerByDBID(dbid uint64) error {

	e := s.GetEntityByDBID(common.EntityType_Player, dbid)
	if e == nil {
		return errors.New("no entity exist")
	}

	return s.DestroyEntity(e.GetID())
}

// 把有脏数据的对象放到space里，统一在结束的时候清掉
func (s *Space) AddIDirty(e iserver.IDirty) {
	// logger.Debug(logger.LogKey_AOI, "AddIDirty:", e)
	s.DirtyTinyList = append(s.DirtyTinyList, e)
}

// 成员管理
//----------------------------------------------------------------
//属性

// IsSpace 是否是空间
func (s *Space) IsSpace() bool {
	return true
}
func (s *Space) GetMapName() string {
	return s.MapName
}

// 设置Loop时的时间
func (s *Space) SetNowTime(n time.Time) {
	s.Entity.SetNowTime(s.TimeTicker.TimeTick(n))
}

// 获取寻路管理器
func (s *Space) GetPathFinder() pathfinder.IPathFinder {
	return s.PathFindermgr
}

// GetTimeStamp 获取当前的时间戳
func (s *Space) GetTimeStamp() uint32 {
	return uint32(s.TimeTicker.GetCurTime() - s.StartTime.UnixMilli())
}

// GetTimer 获取当前的定时器
func (s *Space) GetTimer() *timerwheel.Timer {
	return s.Timer
}

// 返回地图坐标大小，还有每个格子大小
func (s *Space) GetTileSize() (width, height, size int) {
	real := s.GetRealPtr().(IRealSpace)
	minx, miny := real.GetMinPos()
	maxx, maxy := real.GetMaxPos()
	return maxx - minx, maxy - miny, real.GetSize()

}

func (s *Space) GetLevelMiniLocation() *linmath.Vector3 {
	real := s.GetRealPtr().(IRealSpace)
	minx, miny := real.GetMinPos()
	ret := linmath.NewVector3(float32(minx), 0, float32(miny))

	return &ret
}

//属性
//----------------------------------------------------------------
//动态阻挡相关

// 增加可旋转的Box阻挡
func (tb *Space) CreateObstacleOBBBox(doorName string, centPos, halfPos linmath.Vector3, YawDegree int32) {
	tb.PathFindermgr.CreateObstacleOBBBox(doorName, centPos, halfPos, YawDegree)
}

// 增加圆柱体阻挡
func (tb *Space) CreateObstacleCylinder(doorName string, centPos linmath.Vector3, radius, height int32) {
	tb.PathFindermgr.CreateObstacleCylinder(doorName, centPos, radius, height)
}

// 清理掉某个门
func (tb *Space) RemoveObstacle(doorName string) {
	tb.PathFindermgr.RemoveObstacle(doorName)
}

// 更新阻挡信息
func (tb *Space) UpdateObstacle() {
	// 没必要每次都做update，观察下来Update根本做不完
	tb.PathFindermgr.UpdateObstacle()
}

//动态阻挡相关
//----------------------------------------------------------------
